package jeconbond.automata.behaviour;

import java.util.List;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.AutomataUtils;
import jeconbond.automata.behaviour.ListPlanWalker;
import jeconbond.economic.production.IResourceNecessityInformer;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.ResourceUtils;
import jeconbond.economic.systems.market.IMarketResourceRedestributionSystem;

public class MarketAuditWalkStrategy implements IResourceNecessityInformer, IWalkStrategy {
	private ListPlanWalker listPlanWalker;
	private INode root;
	private List<List<IOutcome>> pathList;
	private IMarketResourceRedestributionSystem mrrs;
	
	public MarketAuditWalkStrategy(
			INode root,
			ResourceStorrage inputRS, 
			ResourceStorrage outputRS,
			IMarketResourceRedestributionSystem mrrs
			
	) {
		List<IOutcome> plan = AutomataUtils.easyTracePlan(root);
		listPlanWalker = new ListPlanWalker(
				plan,
				inputRS,
				outputRS
		);
		this.root = root;
		this.mrrs = mrrs;
		pathList = AutomataUtils.dfsRoot2RootPath(root);
	}

	@Override
	public List<InfoItem> listInfoItems() {
		return listPlanWalker.listInfoItems();
	}

	@Override
	public IOutcome doWalk(INode state) {
		if (state == root) {
			doAudit();
		}
		return listPlanWalker.doWalk(state);
	}

	private void doAudit() {
		double bestProfit = -Double.MAX_VALUE;
		List<IOutcome> bestPath = null;
		for (List<IOutcome> path : pathList) {
			double profit = computeProfit(path);
			if (profit > bestProfit) {
				bestProfit = profit;
				bestPath = path;
			}
		}
		if (bestPath != listPlanWalker.getPlan() && bestPath != null) {
			listPlanWalker.setPlan(bestPath);
		}
	}

	private double computeProfit(List<IOutcome> path) {
		double[] costs_ptr = new double[] { 0.0 };
		double[] profit_ptr = new double[] { 0.0 };
		for (IOutcome out : path) {
			addProfCosts(out.getRequirements(), costs_ptr);
			addProfCosts(out.getProducedResources(), profit_ptr);
		}
		return profit_ptr[0] - costs_ptr[0];
	}

	private void addProfCosts(ResourceBasket[] baskets, double[] costs_ptr) {
		for (ResourceBasket rb : baskets) {
			double curCost = ResourceUtils.expectedPrice(rb.type, mrrs);
			if (Double.isNaN(curCost)) {
				curCost = 1.0;
			}
			curCost *= rb.value;
			costs_ptr[0] += curCost;
		}
	}
}
